import django
import json
import asyncio
from asgiref.sync import sync_to_async, async_to_sync
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.core.exceptions import ObjectDoesNotExist

from scheduling.models import GraphInfo
from .models import AlgorithmAPI
from scheduling.dag.graph import Graph, Node, Edge
from scheduling.paradag import dag_run, SequentialProcessor, FullSelector

@sync_to_async(thread_sensitive=True)
def fetch_algorithm_id(api_url):
    """获取 `algorithm_id`"""
    api_entry = AlgorithmAPI.objects.filter(url=api_url, is_deleted=False).first()
    return api_entry.algorithm.id if api_entry else None


@sync_to_async(thread_sensitive=True)
def fetch_graph_data(algorithm_id):
    """获取 `graphInfo` 数据"""
    graph_entry = GraphInfo.objects.filter(id=algorithm_id).first()
    return graph_entry.data if graph_entry else None

async def executeDAG(api_url, graph_updates=None):
    """ 执行 DAG，支持更新 `graph` 结构中 `nodes` 的参数，并确保空参数不会存入 """
    try:
        # ✅ 1. 获取 `algorithm_id`
        api_entry = AlgorithmAPI.objects.filter(url=api_url, is_deleted=False).first()
        if not api_entry:
            return {"status": "error", "message": f"API 配置信息未找到（url={api_url}）"}

        algorithm_id = api_entry.algorithm.id

        # ✅ 2. 获取 `GraphInfo` 数据
        graph_entry = GraphInfo.objects.filter(id=algorithm_id).first()
        if not graph_entry:
            return {"status": "error", "message": f"Graph 数据未找到（algorithm_id={algorithm_id}）"}

        # ✅ 3. 解析 `GraphInfo` 数据，并去掉空 `params`
        graph_data = json.loads(graph_entry.data) if isinstance(graph_entry.data, str) else graph_entry.data
        parsed_data = convert_cells_to_graph(graph_data)

        # ✅ 过滤 `params` 为空的字段
        for node in parsed_data["nodes"]:
            node["params"] = {k: v for k, v in node["params"].items() if v not in [None, "", {}]}

        nodes = parsed_data["nodes"]
        edges = parsed_data["edges"]

        # ✅ 4. 仅更新 `graph` 传入的 `nodes` 参数，并 **确保空参数不会存入**
        if graph_updates and "nodes" in graph_updates:
            update_nodes = graph_updates["nodes"]
            node_map = {node["id"]: node for node in nodes}

            for update in update_nodes:
                node_id = update.get("id")
                new_params = update.get("params", {})

                if node_id in node_map:
                    # ✅ 过滤 `params`，去掉 `None` 或 `""` 的键值对
                    filtered_params = {k: v for k, v in new_params.items() if v not in [None, "", {}]}
                    node_map[node_id]["params"].update(filtered_params)  # ✅ 仅存储非空参数

        # ✅ 5. 构建 DAG 结构
        executor = get_executor()
        nodes_map = {n['id']: Node(n['id'], n['name'], n['params'], n.get('isTrue', True)) for n in nodes}
        edge_objects = [Edge(e['source'], e['target']) for e in edges]
        graph = Graph(nodes_map, edge_objects)

        # ✅ 6. DAG 验证
        if not graph.validateGraph():
            return {"status": "error", "message": "非法的 DAG 结构"}

        # ✅ 7. 执行 DAG 任务，并正确解析 `logs`
        vertices_final, logs = await dag_run(graph.graph, processor=SequentialProcessor(), executor=executor)

        # ✅ 8. 确保 `logs` 可 JSON 序列化
        if logs is None:  # ✅ 避免 `NoneType` 错误
            logs = []

        serialized_logs = []
        for log in logs:
            serialized_logs.append({
                "node_id": str(log.get("node_id", "")),
                "opr": str(log.get("opr", "")),
                "params": str(log.get("params", "")),
                "should_execute": str(log.get("should_execute", "")),
                "status": str(log.get("status", "")),
                "image_url": str(log.get("image_url", ""))
            })

        return {
            "status": "success",
            "message": "DAG 执行完成",
            "api":api_url,
            "log": serialized_logs  # ✅ 返回结构化日志，确保可 JSON 序列化
        }

    except Exception as e:
        return {"status": "error", "message": str(e)}
# ✅ `cells` 转换函数
def convert_cells_to_graph(cells_data):
    """ 确保 `cells_data` 是 JSON 字符串，然后解析，并去掉空 `params` """

    # ✅ 如果 `cells_data` 是 `dict`，转换为 JSON 字符串
    if isinstance(cells_data, dict):
        cells_json = json.dumps(cells_data)
    else:
        cells_json = cells_data

    cells = json.loads(cells_json)  # ✅ 解析 JSON

    nodes = []
    edges = []

    for cell in cells["cells"]:
        if cell["shape"] == "edge":
            edges.append({
                "id": cell["id"],
                "labels": cell.get("defaultLabel", {}).get("attrs", {}).get("label", {}).get("text", ""),
                "source": cell["source"]["cell"],
                "target": cell["target"]["cell"]
            })
        else:
            # ✅ 处理 `params` 字段，去掉值为空的键值对
            params = {
                key: value["value"] if isinstance(value, dict) and "value" in value else value
                for key, value in cell["data"].get("params", {}).items()
                if value not in [None, "", {}]  # ✅ 过滤空参数
            }

            nodes.append({
                "id": cell["id"],
                "name": cell["data"]["name"]["value"],
                "params": params  # ✅ 仅保留 `value` 不为空的字段
            })

    return {"nodes": nodes, "edges": edges}

### 🔹 **支持 Django 3.1+ 的异步视图**
@csrf_exempt
def run_algorithm(request, api_url):
    """同步触发 DAG 执行（仅更新 `graph` 传入的 `nodes` 参数，并过滤空参数）"""
    try:
        print(f"✅ 进入 `run_algorithm()` 方法，api_url = {api_url}")

        # 获取请求参数
        data = json.loads(request.body) if request.body else {}
        graph_updates = data.get("graph", {})  # 直接接收 `graph` 结构

        # 运行 DAG，支持参数更新
        result = async_to_sync(executeDAG)("/"+api_url, graph_updates)

        print(f"✅ `executeDAG()` 执行完成，结果: {result}")
        return JsonResponse(result, safe=False)

    except json.JSONDecodeError:
        return JsonResponse({"status": "error", "message": "无效的 JSON 数据"}, status=400)

    except Exception as e:
        return JsonResponse({"status": "error", "message": str(e)}, status=500)

@csrf_exempt
def create_api(request):
    """创建 API 配置信息"""
    if request.method == 'POST':
        data = json.loads(request.body)
        algorithm_id = data.get('id')
        url = data.get('url')
        description = data.get('description', '')

        algorithm = GraphInfo.objects.filter(id=algorithm_id).first()
        if not algorithm:
            return JsonResponse({"status": "error", "message": "算法 ID 不存在"}, status=400)


        if AlgorithmAPI.objects.filter(algorithm=algorithm, is_deleted=False).exists():
            api = AlgorithmAPI.objects.filter(algorithm=algorithm, is_deleted=False).first()
            if "url" in data:
                api.url = data["url"]
            if "description" in data:
                api.description = data["description"]
            api.save()
            return JsonResponse({
                "status": "success",
                "message": "API 配置信息已更新",
                "api": {
                    "id": algorithm_id,
                    "algorithm_id": api.id,
                    "url": api.url,
                    "description": api.description,
                }
            })

        if AlgorithmAPI.objects.filter(url=url, is_deleted=False).exists():
            return JsonResponse({"status": "error", "message": "该 API URL 已存在"}, status=400)

        api_entry = AlgorithmAPI.objects.create(algorithm=algorithm, url=url, description=description)

        return JsonResponse({
            "status": "success",
            "message": "API 配置信息已创建",
            "api": {
                "id": api_entry.id,
                "algorithm_id": api_entry.algorithm.id,
                "url": api_entry.url,
                "description": api_entry.description,
                "is_deleted": api_entry.is_deleted
            }
        })


@csrf_exempt
def get_api(request, algorithm_id):
    """获取 API 配置信息"""
    api = AlgorithmAPI.objects.filter(algorithm_id=algorithm_id, is_deleted=False).first()
    if not api:
        return JsonResponse({"status": "error", "message": "API 配置信息未找到"})

    return JsonResponse({
        "status": "success",
        "api": {
            "id": api.id,
            "algorithm_id": api.algorithm.id,
            "url": api.url,
            "description": api.description,
            "is_deleted": api.is_deleted
        }
    })


@csrf_exempt
def update_api(request, algorithm_id):
    """修改 API 配置信息"""
    if request.method == 'PUT':
        data = json.loads(request.body)
        api = AlgorithmAPI.objects.filter(algorithm_id=algorithm_id, is_deleted=False).first()

        if not api:
            return JsonResponse({"status": "error", "message": "API 配置信息未找到"}, status=404)

        if "url" in data:
            api.url = data["url"]
        if "description" in data:
            api.description = data["description"]

        api.save()

        return JsonResponse({
            "status": "success",
            "message": "API 配置信息已更新",
            "api": {
                "id": api.id,
                "algorithm_id": api.algorithm.id,
                "url": api.url,
                "description": api.description,
                "is_deleted": api.is_deleted
            }
        })


@csrf_exempt
def delete_api(request, algorithm_id):
    """逻辑删除 API 配置信息"""
    if request.method == 'DELETE':
        api = AlgorithmAPI.objects.filter(algorithm_id=algorithm_id, is_deleted=False).first()

        if not api:
            return JsonResponse({"status": "error", "message": "API 配置信息未找到"}, status=404)

        api.is_deleted = True
        api.save()

        return JsonResponse({"status": "success", "message": "API 配置信息已删除（逻辑删除）"})


def get_executor():
    from scheduling.dag.executer import CVExecuter
    return CVExecuter()


def get_operations():
    from scheduling.dag.executer import OPERATIONS
    return OPERATIONS

def process_graph_data(data):
    """ 解析 DAG 数据，提取 nodes 和 edges，并筛选可配置节点 """
    try:
        if isinstance(data, str):
            data = json.loads(data)

        cells = data.get("cells", [])
        nodes = []
        edges = []
        configurable_nodes = []

        for cell in cells:
            if cell["shape"] == "edge":
                edges.append({
                    "id": cell["id"],
                    "source": cell["source"]["cell"],
                    "target": cell["target"]["cell"],
                    "label": cell.get("defaultLabel", {}).get("attrs", {}).get("label", {}).get("text", ""),
                })
            else:
                params = {
                    key: value["value"] if isinstance(value, dict) and "value" in value else value
                    for key, value in cell["data"].get("params", {}).items()
                }

                node_data = {
                    "id": cell["id"],
                    "name": cell["data"]["name"]["value"],
                    "params": params,
                }
                nodes.append(node_data)

                if params:
                    configurable_nodes.append({
                        "id": cell["id"],
                        "name": cell["data"]["name"]["value"],
                        "params": list(params.keys())
                    })

        return {"nodes": nodes, "edges": edges, "configurable_nodes": configurable_nodes}

    except Exception as e:
        raise ValueError(f"解析数据失败: {str(e)}")

@csrf_exempt
def get_graph_info(request, algorithm_id):
    """ 获取指定 `algorithm_id` 的 DAG 结构信息 """
    try:
        graph_entry = GraphInfo.objects.filter(id=algorithm_id).first()
        if not graph_entry:
            return JsonResponse({"status": "error", "message": f"Graph 数据未找到（algorithm_id={algorithm_id}）"}, status=404)

        graph_data = graph_entry.data
        result = process_graph_data(graph_data)

        return JsonResponse({"status": "success", "graph": result})

    except Exception as e:
        return JsonResponse({"status": "error", "message": str(e)}, status=500)

@csrf_exempt
def get_configurable_nodes(request, algorithm_id):
    """ 获取指定 `algorithm_id` 的可配置节点 """
    try:
        graph_entry = GraphInfo.objects.filter(id=algorithm_id).first()
        if not graph_entry:
            return JsonResponse({"status": "error", "message": f"Graph 数据未找到（algorithm_id={algorithm_id}）"}, status=404)

        graph_data = graph_entry.data
        result = process_graph_data(graph_data)

        return JsonResponse({
            "status": "success",
            "configurable_nodes": result["configurable_nodes"]
        })

    except Exception as e:
        return JsonResponse({"status": "error", "message": str(e)}, status=500)